﻿using System;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Web.UI;
using System.Reflection;
using System.ComponentModel;
using System.Text;
using System.Linq;
using System.Linq.Expressions;

namespace MVCnExt.Web.UI.Ext
{   
    public abstract class ExtComponent : ExtObservable
    {
        ScriptControlDescriptorBase desc;       

        public override IList<ScriptDescriptor> GetScriptDescriptors()
        {
            IList<ScriptDescriptor> list = base.GetScriptDescriptors();

            desc = (ScriptControlDescriptorBase)list[0];

            desc.Type = "Ext.Component";
            List<string> str = new List<string>();
            Type component = typeof(ExtComponent);            
            
            var propertyList = from property in component.GetProperties()
                               where property.DeclaringType == component 
                               select property;            

            propertyList.ToList().ForEach(AddProperty);           

            //renderTo
            if (!String.IsNullOrEmpty(renderTo))
            {
                /* not sure if an exception should be thrown if ScriptManager is null (means this
                 control is a child of an 'items' collection). renderTo doesn't seem to work in this
                 scenario. An exception would make accidental use of this property in this situation
                 easier to troubleshoot. */
                if (String.Compare(renderTo, ID, true) == 0)
                    desc.AddPropertyBase("renderTo", String.Format("'{0}'", ClientID));
                else
                    desc.AddPropertyBase("renderTo", String.Format("'{0}'", GetControlID(renderTo)));
            }

            if (!String.IsNullOrEmpty(ID))
            {
                desc.AddPropertyBase("id", String.Format("'{0}'", ID));
            }

            return list;
        }

        public virtual void AddProperty(PropertyInfo property)
        {
            if (property == null)
                throw new InvalidOperationException(String.Format("Property not found, 'ExtJsComponent.{0}'", property));

            Object value = property.GetValue(this, null);

            if (value == null)
                return;

            if (!(value is String) && !(value is xtype))
                desc.AddPropertyBase(property.Name, value);
            else
                desc.AddPropertyBase(property.Name, String.Format("'{0}'", value));
        }

        public virtual void InstantiateTemplate<T>(List<T> items)
        {
            if (items.Count == 0)
                return;

            PropertyInfo info;
            ITemplate template;

            for (int i = 0; i < items.Count; i++)
            {
                info = items[i].GetType().GetProperty("items");

                if (info != null)
                {                    
                    InstantiateTemplate((List<T>)info.GetValue(items[i], null));
                }

                info = items[i].GetType().GetProperty("html");
                if (info == null)
                    continue;

                template = (ITemplate)info.GetValue(items[i], null);
                if (template != null)
                {
                    Panel Content = new Panel();
                    template.InstantiateIn(Content);
                    Content.ID = string.Format("{0}_El", items[i].GetType().GetProperty("ID").GetValue(items[i],null));
                    Controls.Add(Content);                    
                    items[i].GetType().GetProperty("contentEl").SetValue(items[i], Content.ClientID, null);
                }
            }
        }        
       
        public Boolean? allowDomMove { get; set; }

        public Boolean? autoShow { get; set; }

        public String cls { get; set; }

        public String ctCls { get; set; }

        public Style disabledClass { get; set; }

        public hideMode? hideMode { get; set; }

        public Boolean? hideParent { get; set; }

        public String plugins { get; set; }

        public String renderTo { get; set; }

        public String stateEvents { get; set; }

        public String stateId { get; set; }

        public Boolean? stateful { get; set; }

        public Style style { get; set; }

        public xtype? xtype { get; set; }
    }

    public abstract class ExtComponent<T> : ExtObservable
    {
        private List<ExtComponent<T>> _items = new List<ExtComponent<T>>();

        public override IList<ScriptDescriptor> GetScriptDescriptors()
        {
            IList<ScriptDescriptor> list = base.GetScriptDescriptors();

            ScriptControlDescriptorBase desc = (ScriptControlDescriptorBase)list[0];

            desc.Type = "Ext.Component";

            String[] properties = new String[]{
                "allowDomMove", "autoShow", "cls", "ctCls", "disabledClass",
                "hideMode", "hideParent", "plugins", "stateEvents",
                "stateId", "stateful", "style", "xtype"
            };

            Type component = typeof(ExtComponent<T>);

            foreach (String property in properties)
            {
                PropertyInfo i = component.GetProperty(property);

                if (i == null)
                    throw new InvalidOperationException(String.Format("Property not found, 'ExtJsComponent.{0}'", property));

                Object value = i.GetValue(this, null);

                if (value == null)
                {
                    continue;
                }

                if (!(value is String))
                    desc.AddPropertyBase(property, value);
                else
                    desc.AddPropertyBase(property, String.Format("'{0}'", value));
            }

            //renderTo
            if (!String.IsNullOrEmpty(renderTo))
            {
                /* not sure if an exception should be thrown if ScriptManager is null (means this
                 control is a child of an 'items' collection). renderTo doesn't seem to work in this
                 scenario. An exception would make accidental use of this property in this situation
                 easier to troubleshoot. */
                if (String.Compare(renderTo, ID, true) == 0)
                    desc.AddPropertyBase("renderTo", String.Format("'{0}'", ClientID));
                else
                    desc.AddPropertyBase("renderTo", String.Format("'{0}'", GetControlID(renderTo)));
            }

            // applyTo
            if (!String.IsNullOrEmpty(applyTo))
            {
                if (String.Compare(applyTo, ID, true) == 0)
                    desc.AddPropertyBase("applyTo", String.Format("'{0}'", ClientID));
                else
                    desc.AddPropertyBase("applyTo", String.Format("'{0}'", GetControlID(applyTo)));
            }
            // contentEl
            if (!String.IsNullOrEmpty(contentEl))
            {
                if (String.Compare(contentEl, ID, true) == 0)
                    desc.AddPropertyBase("contentEl", String.Format("'{0}'", ClientID));
                else
                    desc.AddPropertyBase("contentEl", String.Format("'{0}'", GetControlID(contentEl)));
            }


            return list;
        }

        /// <summary>
        /// Instantiate the Body template so we can add child controls
        /// </summary>
        protected override void CreateChildControls()
        {
            //call base method
            base.CreateChildControls();

            CreateExtControls(items);
        }

        public virtual void CreateExtControls(List<ExtComponent<T>> list)
        {
            //verify template exists
            if (list.Count == 0)
                return;

            //instantiate the template in this (Panel)
            Panel containerContent;
            PropertyInfo info;

            for (int i = 0; i < list.Count; i++)
            {
                if ((info = list[i].GetType().GetProperty("items")) != null)
                {
                    if ((info.GetValue(list[i], null)) != null)
                    {
                        list[i].GetType().GetMethod("CreateExtControlss").Invoke(list[i], new object[] { info.GetValue(list[i], null) });
                    }
                }

                info = list[i].GetType().GetProperty("html");
                if (info != null)
                {
                    ITemplate template = (ITemplate)info.GetValue(list[i], null);
                    if (template == null)
                        continue;
                    containerContent = new Panel();
                    template.InstantiateIn(containerContent);
                    containerContent.ID = string.Format("El_{0}_{1}", list[i].GetType().GetProperty("ID"), i);
                    Controls.Add(containerContent);
                    list[i].GetType().GetProperty("contentEl").SetValue(list[i], containerContent.ClientID, null);
                }
            }
        }


        [NotifyParentProperty(true)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<ExtComponent<T>> items
        {
            get
            {
                return _items;
            }
        }


        /// <summary>
        /// The id (ClientId) of the node, a DOM node or an existing Element corresponding to a DIV
        /// </summary>
        public String applyTo { get; set; }

        public String contentEl { get; set; }


        public Boolean? allowDomMove { get; set; }

        public Boolean? autoShow { get; set; }

        public String cls { get; set; }

        public String ctCls { get; set; }

        public Style disabledClass { get; set; }

        public hideMode? hideMode { get; set; }

        public Boolean? hideParent { get; set; }

        public String plugins { get; set; }

        public String renderTo { get; set; }

        public String stateEvents { get; set; }

        public String stateId { get; set; }

        public Boolean? stateful { get; set; }

        public Style style { get; set; }

        public xtype? xtype { get; set; }
    }

    public abstract class ExtConfigComponent : ExtConfigObservable
    {

        public override IList<ScriptDescriptor> GetScriptDescriptors()
        {
            IList<ScriptDescriptor> list = base.GetScriptDescriptors();

            ScriptControlDescriptorBase desc = (ScriptControlDescriptorBase)list[0];

            desc.Type = "Ext.Component";

            String[] properties = new String[]{
                "allowDomMove", "autoShow", "cls", "ctCls", "disabledClass",
                "hideMode", "hideParent", "plugins", "stateEvents",
                "stateId", "stateful", "style", "xtype"
            };

            Type component = typeof(ExtConfigComponent);

            foreach (String property in properties)
            {
                PropertyInfo i = component.GetProperty(property);

                if (i == null)
                    throw new InvalidOperationException(String.Format("Property not found, 'ExtJsComponent.{0}'", property));

                Object value = i.GetValue(this, null);

                if (value == null)
                {
                    continue;
                }

                if (!(value is String) && !(value is xtype))
                    desc.AddPropertyBase(property, value);
                else
                    desc.AddPropertyBase(property, String.Format("'{0}'", value));
            }

            return list;
        }

        public Boolean? allowDomMove { get; set; }

        public Boolean? autoShow { get; set; }

        public String cls { get; set; }

        public String ctCls { get; set; }

        public Style disabledClass { get; set; }

        public hideMode? hideMode { get; set; }

        public Boolean? hideParent { get; set; }

        public String plugins { get; set; }

        public String renderTo { get; set; }

        public String stateEvents { get; set; }

        public String stateId { get; set; }

        public Boolean? stateful { get; set; }

        public Style style { get; set; }

        public xtype? xtype { get; set; }
    }
}
